package cn.edu.gzgs.service;

import cn.edu.gzgs.constant.LoginStatus;
import cn.edu.gzgs.dto.LoginRequestDTO;
import cn.edu.gzgs.dto.UserPermissionsDTO;
import cn.edu.gzgs.entity.User;
import cn.edu.gzgs.entity.UserRole;
import cn.edu.gzgs.mapper.RoleMenuMapper;
import cn.edu.gzgs.mapper.UserMapper;
import cn.edu.gzgs.mapper.UserRoleMapper;
import cn.edu.gzgs.util.JwtUtils;
import cn.edu.gzgs.vo.TeacherLoginVO;
import cn.edu.gzgs.websocket.WebSocketServer;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import javax.security.sasl.AuthenticationException;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AuthService {

    private final UserRoleMapper userRoleMapper;
    private final RoleMenuMapper roleMenuMapper;
    private final UserMapper userMapper;
    private final PasswordEncoder passwordEncoder;
    private final JwtUtils jwtUtils;
    private final LoginLogService loginLogService;
    private final CaptchaService captchaService;

    @Autowired
    public AuthService(UserRoleMapper userRoleMapper, RoleMenuMapper roleMenuMapper, UserMapper userMapper, PasswordEncoder passwordEncoder, JwtUtils jwtUtils, LoginLogService loginLogService, CaptchaService captchaService) {
        this.userRoleMapper = userRoleMapper;
        this.roleMenuMapper = roleMenuMapper;
        this.userMapper = userMapper;
        this.passwordEncoder = passwordEncoder;
        this.jwtUtils = jwtUtils;
        this.loginLogService = loginLogService;
        this.captchaService = captchaService;
    }

    @Transactional(readOnly = true)
    public UserPermissionsDTO getUserPermissions(Long userId) {
        if (userId == null) {
            return new UserPermissionsDTO(new ArrayList<>());
        }

        QueryWrapper<UserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper.eq("user_id", userId).select("role_id");
        List<UserRole> userRoles = userRoleMapper.selectList(userRoleQueryWrapper);
        Set<String> roleIds = userRoles.stream()
                                     .map(userRole -> String.valueOf(userRole.getRoleId()))
                                     .collect(Collectors.toSet());

        if (CollectionUtils.isEmpty(roleIds)) {
            return new UserPermissionsDTO(new ArrayList<>());
        }

        // 根据角色ID获取所有唯一的菜单ID
        Set<Long> menuIds = roleMenuMapper.findMenuIdsByRoleIds(new ArrayList<>(roleIds));

        if (CollectionUtils.isEmpty(menuIds)) {
            return new UserPermissionsDTO(new ArrayList<>());
        }

        // 转成字符串列表返回
        List<String> auths = menuIds.stream()
                .map(String::valueOf)
                .collect(Collectors.toList());

        return new UserPermissionsDTO(auths);
    }

    @Transactional(readOnly = true)
    public TeacherLoginVO login(LoginRequestDTO loginRequest) throws AuthenticationException {
        String userNo = loginRequest.getUserNo();
        User user = null;
        String remarksFromAbnormalCheck = "";

        if (loginRequest == null || userNo == null || loginRequest.getPassword() == null ||
            !StringUtils.hasText(loginRequest.getCaptchaId()) || !StringUtils.hasText(loginRequest.getCaptchaCode())) {
            throw new AuthenticationException("工号、密码或验证码信息不能为空");
        }

        if (!captchaService.validateCaptcha(loginRequest.getCaptchaId(), loginRequest.getCaptchaCode())) {
            loginLogService.recordLoginAttempt(userNo, null, LoginStatus.FAILURE_CAPTCHA, "验证码错误或已过期");
            throw new AuthenticationException("验证码错误或已过期");
        }

        try {
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_no", userNo);
            user = userMapper.selectOne(queryWrapper);

            if (user == null || user.getStatus() == null || user.getStatus() != 1) {
                remarksFromAbnormalCheck = loginLogService.checkAndLogAbnormalLogin(userNo, null, LoginStatus.FAILURE_USER_NOT_FOUND_OR_DISABLED);
                loginLogService.recordLoginAttempt(userNo, null, LoginStatus.FAILURE_USER_NOT_FOUND_OR_DISABLED, "用户不存在或已禁用." + remarksFromAbnormalCheck);
                throw new AuthenticationException("用户不存在或状态异常");
            }

            if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
                remarksFromAbnormalCheck = loginLogService.checkAndLogAbnormalLogin(userNo, user, LoginStatus.FAILURE_PASSWORD_INCORRECT);
                loginLogService.recordLoginAttempt(userNo, user, LoginStatus.FAILURE_PASSWORD_INCORRECT, "密码错误." + remarksFromAbnormalCheck);
                throw new AuthenticationException("密码错误");
            }

            // 登录成功
            remarksFromAbnormalCheck = loginLogService.checkAndLogAbnormalLogin(userNo, user, LoginStatus.SUCCESS);
            loginLogService.recordLoginAttempt(userNo, user, LoginStatus.SUCCESS, "登录成功." + remarksFromAbnormalCheck);

            // 实现单点登录：强制其他相同账号下线
            enforceSingleSignOn(user.getId().toString());

            String token = jwtUtils.generateToken(user.getUserNo());

            return TeacherLoginVO.builder()
                    .id(user.getId())
                    .userNo(user.getUserNo())
                    .name(user.getName())
                    .token(token)
                    .build();

        } catch (AuthenticationException e) {
            throw e;
        } catch (Exception e) {
            remarksFromAbnormalCheck = loginLogService.checkAndLogAbnormalLogin(userNo, user, LoginStatus.FAILURE_UNKNOWN);
            loginLogService.recordLoginAttempt(userNo, user, LoginStatus.FAILURE_UNKNOWN, "登录时发生未知错误: " + e.getMessage() + remarksFromAbnormalCheck);
            throw new AuthenticationException("登录时发生内部错误: " + e.getMessage());
        }
    }

    /**
     * 实现单点登录：强制指定用户的其他连接下线
     */
    private void enforceSingleSignOn(String userId) {
        try {
            // 通过WebSocket强制该用户的其他连接下线
            WebSocketServer.forceUserOffline(userId, "您的账号在别的地方登录，您已被强制下线");
            log.info("用户 {} 的其他连接已被强制下线（单点登录）", userId);
        } catch (Exception e) {
            log.warn("强制用户 {} 下线时发生错误: {}", userId, e.getMessage());
            // 不抛出异常，避免影响正常登录流程
        }
    }
}